//
//  AVIOContext.cpp
//  VideoParse
//
//  Created by xiaerfei on 2020/2/14.
//  Copyright © 2020 erfeixia. All rights reserved.
//

#include "AVIOContext.hpp"
#include <string>
#include <cstdint>


#define FFMIN(a,b) ((a) > (b) ? (b) : (a))

static const int BUFFER_SIZE = 32768;

AVIOContext::AVIOContext() {
    this->buffer_size = BUFFER_SIZE;
    this->buffer  = (unsigned char *) new char[BUFFER_SIZE]();
    this->buf_ptr = this->buffer;
    this->buf_end = this->buffer;
    this->pos     = 0;
    
}

AVIOContext::~AVIOContext() {
    delete [] this->buffer;
}

int AVIOContext:: read_packet(u_int8_t *buf, int buf_size) {
    int len, size1;
    size1 = buf_size;
    
    while (buf_size > 0) {
        len = (int)FFMIN(buf_end - buf_ptr, buf_size);
        if (len == 0) {
            fill_buffer();
            len = (int)(buf_end - buf_ptr);
            if (len == 0)
                break;
        } else {
            memcpy(buf, buf_ptr, len);
            buf += len;
            buf_ptr += len;
            buf_size -= len;
        }
    }
    if (size1 == buf_size) {
        return -1;
    }
    return size1 - buf_size;
}

int AVIOContext:: write_packet(u_int8_t *buf, int buf_size) {
    
    return 0;
}

int64_t AVIOContext:: avio_seek(int64_t offset, int whence) {
    int64_t offset1;
    int64_t pos;
    long buffer_size;
    
    whence &= ~AVSEEK_FORCE;
    
    if ((whence & AVSEEK_SIZE)) return this->buffer_size;
    // pos 为 buffer 开始时在文件中的偏移
    /*
     *                                         offset1   offset pos
     *                         buffer          buf_ptr     | buf_end
     *                            +---------------+-----------------------+
     *                            |/ / / / / / / /|/ / / / / / /|         |
     *  read buffer:              |/ / consumed / | to be read /|         |
     *                            |/ / / / / / / /|/ / / / / / /|         |
     *                            +---------------+-----------------------+
     *                            |---------buffer_size---------|
     */
    
    buffer_size = buf_end - buffer;
    pos = this->pos - buffer_size;
    
    if (whence == SEEK_CUR) {
        offset1 = pos + (buf_ptr - buffer);
        if (offset == 0) { return offset1; }
        offset += offset1;
        offset1 = offset - pos;
        
        if (offset1 >= 0 && offset1 <= buffer_size) {
            buf_ptr = buffer + offset1;
        } else if (offset1 >= 0 && offset1 > buffer_size) {
            while (this->pos < offset && !this->eof_reached) {
                fill_buffer();
            }
            if (this->eof_reached) {
                return -1;
            }
            this->buf_ptr = this->buf_end - (this->pos - offset);
        } else if (offset1 < 0 && -offset1 <= (this->buf_ptr - buffer)) {
            this->buf_ptr += offset1;
        } else if (offset1 < 0 && -offset1 <= (this->buf_ptr - buffer)) {
            while ((this->pos - buffer_size) >= 0 && (this->pos - buffer_size) < offset && !this->eof_reached) {
                fill_buffer_back();
            }
            if (this->eof_reached) {
                return -1;
            }
            this->buf_ptr = this->buf_end - (this->pos - offset);
        }
        return offset;
    } else if (whence == SEEK_SET) {
        
        // SEEK_SET: 表示 offset 是相对 原始 buffer 开始位置的
        /*
                |                          offset
                                  ptr                  end
                                                       pos
         buffer |------------------|--------------------|
         
         
         0<= offset <= end
         end < offset
         
         */
        
        if (offset >= 0 && offset <= buffer_size) {
            this->buf_ptr = buffer + offset;
            return pos + offset;
        } else if (offset >= 0 && offset > buffer_size) {
            long set = this->pos + offset - buffer_size;
            while (this->pos > set) {
                fill_buffer();
            }
            this->buf_ptr = this->buf_end - (this->pos - offset);
            return set;
        }
    } else if (whence == SEEK_END) {
        // SEEK_END: 表示 offset 是相对 原始 buffer 结束位置的
        long set = this->pos + offset;
        while (this->pos > set) {
            fill_buffer();
        }
        this->buf_ptr = this->buf_end - (this->pos - offset);
        return set;
    }
    return -1;
}

int AVIOContext::avio_r8() {
    if (this->buf_ptr >= this->buf_end)
        this->fill_buffer();
    if (this->buf_ptr < this->buf_end)
        return *this->buf_ptr++;
    return 0;
}

unsigned int AVIOContext::avio_rl16() {
    unsigned int val;
    val = avio_r8();
    val |= avio_r8() << 8;
    return val;
}

unsigned int AVIOContext::avio_rl24() {
    unsigned int val;
    val = avio_rl16();
    val |= avio_r8() << 16;
    return val;
}

unsigned int AVIOContext::avio_rl32() {
    unsigned int val;
    val = avio_rl16();
    val |= avio_rl16() << 16;
    return val;
}

unsigned long long AVIOContext::avio_rl64() {
    unsigned long long val;
    val = (unsigned long long)avio_rl32();
    val |= (unsigned long long)avio_rl32() << 32;
    return val;
}

unsigned int AVIOContext::avio_rb16() {
    unsigned int val;
    val = avio_r8() << 8;
    val |= avio_r8();
    return val;
}

unsigned int AVIOContext::avio_rb24() {
    unsigned int val;
    val = avio_rb16() << 8;
    val |= avio_r8();
    return val;
}

unsigned int AVIOContext::avio_rb32() {
    unsigned int val;
    val = avio_rb16() << 16;
    val |= avio_rb16();
    return val;
}

unsigned long long AVIOContext::avio_rb64() {
    unsigned long long val;
    val = (unsigned long long)avio_rb32() << 32;
    val |= (unsigned long long)avio_rb32();
    return val;
}

int64_t AVIOContext::avio_skip(int64_t offset) {
    return avio_seek(offset, SEEK_CUR);
}

int64_t AVIOContext::avio_tell() {
    return avio_seek(0, SEEK_CUR);
}

#pragma mark - private

long AVIOContext::fill_buffer() {
    long size = this->ffRRL->url_read((char *)this->buffer, BUFFER_SIZE);
    this->pos += size;
    this->eof_reached = BUFFER_SIZE != size;
    this->buf_ptr = this->buffer;
    this->buf_end = this->buf_ptr + size;
    return size;
}

long AVIOContext::fill_buffer(long offset) {
    this->ffRRL->url_seek(offset, 0);
    return 0;
}

long AVIOContext::fill_buffer_back() {
    long rPos = this->pos - BUFFER_SIZE;
    this->ffRRL->url_seek(rPos, 0);
    if (this->pos - BUFFER_SIZE >= 0) {
        this->pos -= BUFFER_SIZE;
    }
    return 0;
}


